/**
 * Copyright (c) 2022 Beijing Jiaotong University
 * PhotLab is licensed under [Open Source License].
 * You can use this software according to the terms and conditions of the [Open Source License].
 * You may obtain a copy of [Open Source License] at: [https://open.source.license/]
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 *
 * See the [Open Source License] for more details.
 *
 * Author: Zhenjie Wei
 * Created: Mar. 13, 2023
 * Supported by: National Key Research and Development Program of China
 */

/* eslint-disable @typescript-eslint/no-explicit-any */

import { Disposable } from '../disposable'
import { ICommand } from './command'

export const commandManager = new class CommandManager extends Disposable {
    private readonly _commands: Map<string, ICommand<any>>
    private _history: ICommand<any>[]
    private _redo: ICommand<any>[]

    constructor() {
        super()
        this._commands = new Map()
        this._history = []
        this._redo = []
    }

    public registerCommand<T>(cmdId: string, exec: (args: any) => Promise<T>) {
        this._commands.set(cmdId, {
            id: cmdId,
            exec,
            undo: (args: any) => {
                return Promise.resolve(null)
            },
        })
    }

    public executeCommand<T>(cmdId: string, ...args: any[]): Promise<T | null> {
        const command = this._commands.get(cmdId)
        if (command) {
            this._history.push(command)
            return command.exec(...args)
        }
        console.log('Command Not Found.', cmdId)
        return Promise.resolve(null)
    }

    public undoCommand() {
        if (this._history.length > 0) {
            const command = this._history.pop()
            if (command) {
                command.undo()
                this._redo.push(command)
            }
        }
    }

    public redoCommand() {
        if (this._redo.length > 0) {
            const command = this._redo.pop()
            if (command) {
                command.exec()
                this._history.push(command)
            }
        }
    }

    public clear() {
        this._history = []
        this._redo = []
    }
}